package com.chuhe.starry.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.chuhe.common.constans.MyConstants;
import com.chuhe.common.domain.dto.MessageSendDto;
import com.chuhe.common.domain.dto.TokenUserInfoDto;
import com.chuhe.common.domain.entity.UserContact;
import com.chuhe.common.domain.entity.UserInfo;
import com.chuhe.common.domain.entity.UserInfoBeauty;
import com.chuhe.common.domain.enums.*;
import com.chuhe.common.domain.query.PaginationResultVO;
import com.chuhe.common.domain.query.SimplePage;
import com.chuhe.common.domain.vo.req.AdminLoadUserReq;
import com.chuhe.common.domain.vo.req.LoginReq;
import com.chuhe.common.domain.vo.req.RegisterReq;
import com.chuhe.common.domain.vo.req.SaveUserInfoReq;
import com.chuhe.common.domain.vo.resp.ApiResult;
import com.chuhe.common.domain.vo.resp.GetUserInfoResp;
import com.chuhe.common.domain.vo.resp.LoginResp;
import com.chuhe.common.exception.CommonErrorEnum;
import com.chuhe.common.serviceutil.LockService;
import com.chuhe.common.utils.*;
import com.chuhe.starry.dao.UserContactDao;
import com.chuhe.starry.dao.UserInfoBeautyDao;
import com.chuhe.starry.dao.UserInfoDao;
import com.chuhe.starry.service.UserInfoService;
import com.chuhe.starry.service.adapter.UserContactAdapter;
import com.chuhe.starry.service.adapter.UserInfoAdapter;
import com.chuhe.starry.service.adapter.UserInfoBeautyAdapter;
import com.chuhe.starry.service.adapter.UserLoginRespAdapter;
import com.chuhe.starry.websocket.MessageHandler;
import com.wf.captcha.ArithmeticCaptcha;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;

/**
 * @Author: 楚河
 * @Date: 2025年05月08日22:13
 */
@Service
public class UserInfoServiceImpl implements UserInfoService {
    @Autowired
    private UserInfoDao userInfoDao;
    @Autowired
    private UserInfoBeautyDao userInfoBeautyDao;
    @Autowired
    private UserContactDao userContactDao;
    @Autowired
    private AppConfig appConfig;
    @Autowired
    private RedisComponet redisComponet;
    @Autowired
    private LockService lockService;
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private MessageHandler messageHandler;

    /**
     * 生成验证码
     * @Author: 楚河
     * @Date: 2025-05-11 11:05
     */
    @Override
    public Map<String, String> makeCheckCode() {
        // 创建尺寸为 100x43 像素的算术验证码（如 3+5=?）
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(100, 42);
        String code = captcha.text();
        // 生成验证码密钥
        String checkCodeKey = UUID.randomUUID().toString();
        // Redis 存储验证码
        redisUtils.setex(MyConstants.REDIS_KEY_CHECK_CODE + checkCodeKey,code, MyConstants.SECOND_60 * 10);
        // 将验证码图片转为 Base64 编码字符串
        String checkCodeBase64 = captcha.toBase64();
        // 封装结果返回
        Map<String,String> result = new HashMap<>();
        result.put("checkCode",checkCodeBase64);
        result.put("checkCodeKey",checkCodeKey);
        return result;
    }

    /**
     * 注册
     * @Author: 楚河
     * @Date: 2025-05-11 10:52
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ApiResult<?> register(RegisterReq registerReq) {
        try {
            // 验证码校验
            String storedCode = (String)redisUtils.get(MyConstants.REDIS_KEY_CHECK_CODE + registerReq.getCheckCodeKey());
            AssertUtil.isEmpty(storedCode,"验证码已过期");
            AssertUtil.isNotTrue(storedCode.equalsIgnoreCase(registerReq.getCheckCode()),"图片验证码不正确");
            // 邮箱是否已经存在
            UserInfo userInfo = userInfoDao.selectEmail(registerReq.getEmail());
            AssertUtil.isNotEmpty(userInfo,"邮箱已存在");
            // 生成UserId
            String userId = StringTools.getUserId();
            // 查询邮箱是否需要设置靓号
            UserInfoBeauty beautyAccount = userInfoBeautyDao.selectEmail(registerReq.getEmail());
            Boolean useBeautyAccount = null != beautyAccount && BeautyAccountStatusEnum.NO_USE.getStatus().equals(beautyAccount.getStatus().intValue());
            if(useBeautyAccount){
                userId = UserContactTypeEnum.USER.getPrefix() + beautyAccount.getUserId();
            }
            // 注册信息入库
            UserInfo insert = UserInfoAdapter.buildRegisterInfo(registerReq,userId);
            userInfoDao.save(insert);
            // 更新靓号状态
            if(useBeautyAccount){
                UserInfoBeauty userInfoBeauty = UserInfoBeautyAdapter.buildUserBeautyInfo(beautyAccount.getUserId());
                userInfoBeautyDao.updateByUid(userInfoBeauty);
            }
            // 创建机器人好友...
            userContactDao.addContact4Robot(userId);
        }finally {
            // 无论结果如何 删除Redis验证码
            redisUtils.delete(MyConstants.REDIS_KEY_CHECK_CODE + registerReq.getCheckCodeKey());
        }
        return ApiResult.success();
    }
    /**
     * 登录
     * @Author: 楚河
     * @Date: 2025-05-11 15:55
     */
    @Override
    public LoginResp login(LoginReq loginReq) {
        // ✅ 添加 return 承接锁方法返回值
        return lockService.executeWithLock("Login_lock_key" + loginReq.getEmail(), () -> {
            try {
                // 验证码校验逻辑...
                String code = (String)redisUtils.get(MyConstants.REDIS_KEY_CHECK_CODE + loginReq.getCheckCodeKey());
                boolean equals = Objects.equals(code, loginReq.getCheckCode());
                AssertUtil.isNotTrue(equals, "图片验证码不正确");

                // 用户校验逻辑...
                UserInfo userInfo = userInfoDao.selectEmail(loginReq.getEmail());
                AssertUtil.isNotTrue((Objects.isNull(userInfo) || loginReq.getPassword()
                        .equals(userInfo.getPassword())), "账号或密码错误");
                AssertUtil.isTrue(Objects.equals(UserStatusEnum.DISABLE.getStatus()
                        ,userInfo.getStatus().intValue()), "账号已被禁用");

                // 联系人处理...(先清空，再重新拉取，确保是最新)
                UserContact userContact = UserContactAdapter.buildUserContact(userInfo.getUserId());
                List<String> contactList = userContactDao.selectContactList(userContact);
                redisUtils.delete(MyConstants.REDIS_KEY_USER_CONTACT + userInfo.getUserId());
                if (contactList.size() > MyConstants.LENGTH_0) {
                    redisUtils.lpushAll(MyConstants.REDIS_KEY_USER_CONTACT + userInfo.getUserId(),contactList, MyConstants.REDIS_KEY_TOKEN_EXPIRES);
                }

                // Token处理...
                TokenUserInfoDto tokenUserInfoDto = getTokenUserInfoDto(userInfo);
                Long lastHertBeat = (Long)redisUtils.get(MyConstants.REDIS_KEY_WS_USER_HEART_BEAT + userInfo.getUserId());
                AssertUtil.isNotEmpty(lastHertBeat, "此账号已经在别处登录，请退出后再登录");

                String token = DigestUtil.md5Hex(tokenUserInfoDto.getUserId() + RandomUtil.randomString(MyConstants.LENGTH_20));
                tokenUserInfoDto.setToken(token);
                redisComponet.saveTokenUserInfoDto(tokenUserInfoDto);

                // ✅ 确保返回类型与 login 方法声明一致
                return UserLoginRespAdapter.buildUserLoginRespAdapter(userInfo, tokenUserInfoDto);
            } finally {
                redisUtils.delete(MyConstants.REDIS_KEY_CHECK_CODE + loginReq.getCheckCodeKey());
            }
        });
    }

    //获取token
    private TokenUserInfoDto getTokenUserInfoDto(UserInfo userInfo) {
        TokenUserInfoDto tokenUserInfoDto = new TokenUserInfoDto();
        tokenUserInfoDto.setUserId(userInfo.getUserId());
        tokenUserInfoDto.setNickName(userInfo.getNickName());
        String adminEmails = appConfig.getAdminEmails();
        if(Objects.nonNull(adminEmails) && ArrayUtils.contains(adminEmails.split(","),userInfo.getEmail())){
            tokenUserInfoDto.setAdmin(true);
        }else {
            tokenUserInfoDto.setAdmin(false);
        }
        return tokenUserInfoDto;
    }

    /**
     * 获取用户信息
     * @Author: 楚河
     * @Date: 2025-05-17 21:51
     */
    @Override
    public GetUserInfoResp getUserInfo(TokenUserInfoDto userInfo) {
        UserInfo user = userInfoDao.selectByUserId(userInfo.getUserId());
        GetUserInfoResp userInfoResp = new GetUserInfoResp();
        BeanUtils.copyProperties(user, userInfoResp);
        userInfoResp.setAdmin(userInfo.getAdmin());
        return userInfoResp;
    }

    /**
     * 保存用户信息
     * @Author: 楚河
     * @Date: 2025-05-17 22:26
     */
    @Override
    public void saveUserInfo(HttpServletRequest request,
                             UserInfo userInfo,
                             MultipartFile avatarFile,
                             MultipartFile avatarCover) throws IOException {
        TokenUserInfoDto userInfoDto = redisComponet.getTokenUserInfoByToken(request);
        userInfo.setUserId(userInfoDto.getUserId());
        userInfo.setPassword(null);
        userInfo.setStatus(null);
        userInfo.setCreateTime(null);
        userInfo.setLastLoginTime(null);
        userInfoDao.updateUserInfo(userInfo,avatarFile,avatarCover);
        //如果修改了昵称则重新加入缓存
        if(!userInfoDto.getNickName().equals(userInfo.getNickName())){
            userInfoDto.setNickName(userInfo.getNickName());
            resetTokenUserInfo(request,userInfoDto);
        }
    }

    /**
     * 保存用户信息
     * @Author: 楚河
     * @Date: 2025-05-17 22:33
     */
    private void resetTokenUserInfo(HttpServletRequest request, TokenUserInfoDto userInfoDto) {
        String token = request.getHeader("token");
        redisUtils.setex(MyConstants.REDIS_KEY_WS_TOKEN + token,userInfoDto,MyConstants.REDIS_KEY_EXPIRES_DAY);
    }

    /**
     * 修改密码
     * @Author: 楚河
     * @Date: 2025-05-17 22:43
     */
    @Override
    public void updatePassword(TokenUserInfoDto userInfoDto, String password) {
        userInfoDao.updatePassword(userInfoDto.getUserId(),password);
    }

    /**
     * 管理端 查询所有用户
     * @Author: 楚河
     * @Date: 2025-05-18 10:31
     */
    @Override
    public PaginationResultVO<UserInfo> findListByPage(AdminLoadUserReq req) {
        int count = userInfoDao.selectUserCount();
        int pageSize = req.getPageSize() == null ? PageSize.SIZE15.getSize() : req.getPageSize();

        SimplePage page = new SimplePage(req.getPageNo(),count,pageSize);
        req.setSimplePage(page);
        List<UserInfo> list = userInfoDao.selectAllUser(req);
        PaginationResultVO<UserInfo> resultVO = new PaginationResultVO<>(count, page.getPageSize(), page.getPageNo(), page.getPageTotal(), list);
        return resultVO;
    }

    /**
     * 修改用户在线状态
     * @Author: 楚河
     * @Date: 2025-05-18 10:59
     */
    @Override
    public void updateUserStatus(Integer status, String userId) {
        UserStatusEnum statusEnum = UserStatusEnum.getByStatus(status);
        AssertUtil.isEmpty(statusEnum, CommonErrorEnum.CODE_600);
        userInfoDao.updateUserStatusByUserId(status,userId);
    }

    /**
     * 强制下线
     * @Author: 楚河
     * @Date: 2025-05-24 17:15
     */
    @Override
    public void forceOffLine(String userId) {
        MessageSendDto sendDto = new MessageSendDto();
        sendDto.setContactType(UserContactTypeEnum.USER.getType());
        sendDto.setMessageType(MessageTypeEnum.FORCE_OFF_LINE.getType());
        sendDto.setContactId(userId);
        messageHandler.sendMessage(sendDto);
    }
}
